home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / Common / DXUTsound.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  54.2 KB  |  1,568 lines

  1. //-----------------------------------------------------------------------------
  2. // File: DXUTsound.cpp
  3. //
  4. // Desc: DirectSound framework classes for reading and writing wav files and
  5. //       playing them in DirectSound buffers. Feel free to use this class 
  6. //       as a starting point for adding extra functionality.
  7. //
  8. // Copyright (c) Microsoft Corp. All rights reserved.
  9. //-----------------------------------------------------------------------------
  10. #define STRICT
  11. #include "dxstdafx.h"
  12. #include <mmsystem.h>
  13. #include <dsound.h>
  14. #include "DXUTsound.h"
  15.  
  16.  
  17. //-----------------------------------------------------------------------------
  18. // Name: CSoundManager::CSoundManager()
  19. // Desc: Constructs the class
  20. //-----------------------------------------------------------------------------
  21. CSoundManager::CSoundManager()
  22. {
  23.     m_pDS = NULL;
  24. }
  25.  
  26.  
  27. //-----------------------------------------------------------------------------
  28. // Name: CSoundManager::~CSoundManager()
  29. // Desc: Destroys the class
  30. //-----------------------------------------------------------------------------
  31. CSoundManager::~CSoundManager()
  32. {
  33.     SAFE_RELEASE( m_pDS ); 
  34. }
  35.  
  36.  
  37. //-----------------------------------------------------------------------------
  38. // Name: CSoundManager::Initialize()
  39. // Desc: Initializes the IDirectSound object and also sets the primary buffer
  40. //       format.  This function must be called before any others.
  41. //-----------------------------------------------------------------------------
  42. HRESULT CSoundManager::Initialize( HWND  hWnd, 
  43.                                    DWORD dwCoopLevel )
  44. {
  45.     HRESULT             hr;
  46.  
  47.     SAFE_RELEASE( m_pDS );
  48.  
  49.     // Create IDirectSound using the primary sound device
  50.     if( FAILED( hr = DirectSoundCreate8( NULL, &m_pDS, NULL ) ) )
  51.         return DXUT_ERR( L"DirectSoundCreate8", hr );
  52.  
  53.     // Set DirectSound coop level 
  54.     if( FAILED( hr = m_pDS->SetCooperativeLevel( hWnd, dwCoopLevel ) ) )
  55.         return DXUT_ERR( L"SetCooperativeLevel", hr );   
  56.  
  57.     return S_OK;
  58. }
  59.  
  60.  
  61. //-----------------------------------------------------------------------------
  62. // Name: CSoundManager::SetPrimaryBufferFormat()
  63. // Desc: Set primary buffer to a specified format 
  64. //       !WARNING! - Setting the primary buffer format and then using this
  65. //                   same dsound object for DirectMusic messes up DirectMusic! 
  66. //       For example, to set the primary buffer format to 22kHz stereo, 16-bit
  67. //       then:   dwPrimaryChannels = 2
  68. //               dwPrimaryFreq     = 22050, 
  69. //               dwPrimaryBitRate  = 16
  70. //-----------------------------------------------------------------------------
  71. HRESULT CSoundManager::SetPrimaryBufferFormat( DWORD dwPrimaryChannels, 
  72.                                                DWORD dwPrimaryFreq, 
  73.                                                DWORD dwPrimaryBitRate )
  74. {
  75.     HRESULT             hr;
  76.     LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;
  77.  
  78.     if( m_pDS == NULL )
  79.         return CO_E_NOTINITIALIZED;
  80.  
  81.     // Get the primary buffer 
  82.     DSBUFFERDESC dsbd;
  83.     ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
  84.     dsbd.dwSize        = sizeof(DSBUFFERDESC);
  85.     dsbd.dwFlags       = DSBCAPS_PRIMARYBUFFER;
  86.     dsbd.dwBufferBytes = 0;
  87.     dsbd.lpwfxFormat   = NULL;
  88.        
  89.     if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ) ) )
  90.         return DXUT_ERR( L"CreateSoundBuffer", hr );
  91.  
  92.     WAVEFORMATEX wfx;
  93.     ZeroMemory( &wfx, sizeof(WAVEFORMATEX) ); 
  94.     wfx.wFormatTag      = (WORD) WAVE_FORMAT_PCM; 
  95.     wfx.nChannels       = (WORD) dwPrimaryChannels; 
  96.     wfx.nSamplesPerSec  = (DWORD) dwPrimaryFreq; 
  97.     wfx.wBitsPerSample  = (WORD) dwPrimaryBitRate; 
  98.     wfx.nBlockAlign     = (WORD) (wfx.wBitsPerSample / 8 * wfx.nChannels);
  99.     wfx.nAvgBytesPerSec = (DWORD) (wfx.nSamplesPerSec * wfx.nBlockAlign);
  100.  
  101.     if( FAILED( hr = pDSBPrimary->SetFormat(&wfx) ) )
  102.         return DXUT_ERR( L"SetFormat", hr );
  103.  
  104.     SAFE_RELEASE( pDSBPrimary );
  105.  
  106.     return S_OK;
  107. }
  108.  
  109.  
  110. //-----------------------------------------------------------------------------
  111. // Name: CSoundManager::Get3DListenerInterface()
  112. // Desc: Returns the 3D listener interface associated with primary buffer.
  113. //-----------------------------------------------------------------------------
  114. HRESULT CSoundManager::Get3DListenerInterface( LPDIRECTSOUND3DLISTENER* ppDSListener )
  115. {
  116.     HRESULT             hr;
  117.     DSBUFFERDESC        dsbdesc;
  118.     LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;
  119.  
  120.     if( ppDSListener == NULL )
  121.         return E_INVALIDARG;
  122.     if( m_pDS == NULL )
  123.         return CO_E_NOTINITIALIZED;
  124.  
  125.     *ppDSListener = NULL;
  126.  
  127.     // Obtain primary buffer, asking it for 3D control
  128.     ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) );
  129.     dsbdesc.dwSize = sizeof(DSBUFFERDESC);
  130.     dsbdesc.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_PRIMARYBUFFER;
  131.     if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbdesc, &pDSBPrimary, NULL ) ) )
  132.         return DXUT_ERR( L"CreateSoundBuffer", hr );
  133.  
  134.     if( FAILED( hr = pDSBPrimary->QueryInterface( IID_IDirectSound3DListener, 
  135.                                                   (VOID**)ppDSListener ) ) )
  136.     {
  137.         SAFE_RELEASE( pDSBPrimary );
  138.         return DXUT_ERR( L"QueryInterface", hr );
  139.     }
  140.  
  141.     // Release the primary buffer, since it is not need anymore
  142.     SAFE_RELEASE( pDSBPrimary );
  143.  
  144.     return S_OK;
  145. }
  146.  
  147.  
  148. //-----------------------------------------------------------------------------
  149. // Name: CSoundManager::Create()
  150. // Desc: 
  151. //-----------------------------------------------------------------------------
  152. HRESULT CSoundManager::Create( CSound** ppSound, 
  153.                                LPWSTR strWaveFileName, 
  154.                                DWORD dwCreationFlags, 
  155.                                GUID guid3DAlgorithm,
  156.                                DWORD dwNumBuffers )
  157. {
  158.     HRESULT hr;
  159.     HRESULT hrRet = S_OK;
  160.     DWORD   i;
  161.     LPDIRECTSOUNDBUFFER* apDSBuffer     = NULL;
  162.     DWORD                dwDSBufferSize = NULL;
  163.     CWaveFile*           pWaveFile      = NULL;
  164.  
  165.     if( m_pDS == NULL )
  166.         return CO_E_NOTINITIALIZED;
  167.     if( strWaveFileName == NULL || ppSound == NULL || dwNumBuffers < 1 )
  168.         return E_INVALIDARG;
  169.  
  170.     apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers];
  171.     if( apDSBuffer == NULL )
  172.     {
  173.         hr = E_OUTOFMEMORY;
  174.         goto LFail;
  175.     }
  176.  
  177.     pWaveFile = new CWaveFile();
  178.     if( pWaveFile == NULL )
  179.     {
  180.         hr = E_OUTOFMEMORY;
  181.         goto LFail;
  182.     }
  183.  
  184.     pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ );
  185.  
  186.     if( pWaveFile->GetSize() == 0 )
  187.     {
  188.         // Wave is blank, so don't create it.
  189.         hr = E_FAIL;
  190.         goto LFail;
  191.     }
  192.  
  193.     // Make the DirectSound buffer the same size as the wav file
  194.     dwDSBufferSize = pWaveFile->GetSize();
  195.  
  196.     // Create the direct sound buffer, and only request the flags needed
  197.     // since each requires some overhead and limits if the buffer can 
  198.     // be hardware accelerated
  199.     DSBUFFERDESC dsbd;
  200.     ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
  201.     dsbd.dwSize          = sizeof(DSBUFFERDESC);
  202.     dsbd.dwFlags         = dwCreationFlags;
  203.     dsbd.dwBufferBytes   = dwDSBufferSize;
  204.     dsbd.guid3DAlgorithm = guid3DAlgorithm;
  205.     dsbd.lpwfxFormat     = pWaveFile->m_pwfx;
  206.     
  207.     // DirectSound is only guarenteed to play PCM data.  Other
  208.     // formats may or may not work depending the sound card driver.
  209.     hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[0], NULL );
  210.  
  211.     // Be sure to return this error code if it occurs so the
  212.     // callers knows this happened.
  213.     if( hr == DS_NO_VIRTUALIZATION )
  214.         hrRet = DS_NO_VIRTUALIZATION;
  215.             
  216.     if( FAILED(hr) )
  217.     {
  218.         // DSERR_BUFFERTOOSMALL will be returned if the buffer is
  219.         // less than DSBSIZE_FX_MIN and the buffer is created
  220.         // with DSBCAPS_CTRLFX.
  221.         
  222.         // It might also fail if hardware buffer mixing was requested
  223.         // on a device that doesn't support it.
  224.         DXUT_ERR( L"CreateSoundBuffer", hr );
  225.                     
  226.         goto LFail;
  227.     }
  228.  
  229.     // Default to use DuplicateSoundBuffer() when created extra buffers since always 
  230.     // create a buffer that uses the same memory however DuplicateSoundBuffer() will fail if 
  231.     // DSBCAPS_CTRLFX is used, so use CreateSoundBuffer() instead in this case.
  232.     if( (dwCreationFlags & DSBCAPS_CTRLFX) == 0 )
  233.     {
  234.         for( i=1; i<dwNumBuffers; i++ )
  235.         {
  236.             if( FAILED( hr = m_pDS->DuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) )
  237.             {
  238.                 DXUT_ERR( L"DuplicateSoundBuffer", hr );
  239.                 goto LFail;
  240.             }
  241.         }
  242.     }
  243.     else
  244.     {
  245.         for( i=1; i<dwNumBuffers; i++ )
  246.         {
  247.             hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[i], NULL );
  248.             if( FAILED(hr) )
  249.             {
  250.                 DXUT_ERR( L"CreateSoundBuffer", hr );
  251.                 goto LFail;
  252.             }
  253.         }
  254.    }
  255.     
  256.     // Create the sound
  257.     *ppSound = new CSound( apDSBuffer, dwDSBufferSize, dwNumBuffers, pWaveFile, dwCreationFlags );
  258.     
  259.     SAFE_DELETE_ARRAY( apDSBuffer );
  260.     return hrRet;
  261.  
  262. LFail:
  263.     // Cleanup
  264.     SAFE_DELETE( pWaveFile );
  265.     SAFE_DELETE_ARRAY( apDSBuffer );
  266.     return hr;
  267. }
  268.  
  269.  
  270. //-----------------------------------------------------------------------------
  271. // Name: CSoundManager::CreateFromMemory()
  272. // Desc: 
  273. //-----------------------------------------------------------------------------
  274. HRESULT CSoundManager::CreateFromMemory( CSound** ppSound, 
  275.                                         BYTE* pbData,
  276.                                         ULONG  ulDataSize,
  277.                                         LPWAVEFORMATEX pwfx,
  278.                                         DWORD dwCreationFlags, 
  279.                                         GUID guid3DAlgorithm,
  280.                                         DWORD dwNumBuffers )
  281. {
  282.     HRESULT hr;
  283.     DWORD   i;
  284.     LPDIRECTSOUNDBUFFER* apDSBuffer     = NULL;
  285.     DWORD                dwDSBufferSize = NULL;
  286.     CWaveFile*           pWaveFile      = NULL;
  287.  
  288.     if( m_pDS == NULL )
  289.         return CO_E_NOTINITIALIZED;
  290.     if( pbData == NULL || ppSound == NULL || dwNumBuffers < 1 )
  291.         return E_INVALIDARG;
  292.  
  293.     apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers];
  294.     if( apDSBuffer == NULL )
  295.     {
  296.         hr = E_OUTOFMEMORY;
  297.         goto LFail;
  298.     }
  299.  
  300.     pWaveFile = new CWaveFile();
  301.     if( pWaveFile == NULL )
  302.     {
  303.         hr = E_OUTOFMEMORY;
  304.         goto LFail;
  305.     }
  306.  
  307.     pWaveFile->OpenFromMemory( pbData,ulDataSize, pwfx, WAVEFILE_READ );
  308.  
  309.  
  310.     // Make the DirectSound buffer the same size as the wav file
  311.     dwDSBufferSize = ulDataSize;
  312.  
  313.     // Create the direct sound buffer, and only request the flags needed
  314.     // since each requires some overhead and limits if the buffer can 
  315.     // be hardware accelerated
  316.     DSBUFFERDESC dsbd;
  317.     ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
  318.     dsbd.dwSize          = sizeof(DSBUFFERDESC);
  319.     dsbd.dwFlags         = dwCreationFlags;
  320.     dsbd.dwBufferBytes   = dwDSBufferSize;
  321.     dsbd.guid3DAlgorithm = guid3DAlgorithm;
  322.     dsbd.lpwfxFormat     = pwfx;
  323.  
  324.     if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[0], NULL ) ) )
  325.     {
  326.         DXUT_ERR( L"CreateSoundBuffer", hr );
  327.         goto LFail;
  328.     }
  329.  
  330.     // Default to use DuplicateSoundBuffer() when created extra buffers since always 
  331.     // create a buffer that uses the same memory however DuplicateSoundBuffer() will fail if 
  332.     // DSBCAPS_CTRLFX is used, so use CreateSoundBuffer() instead in this case.
  333.     if( (dwCreationFlags & DSBCAPS_CTRLFX) == 0 )
  334.     {
  335.         for( i=1; i<dwNumBuffers; i++ )
  336.         {
  337.             if( FAILED( hr = m_pDS->DuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) )
  338.             {
  339.                 DXUT_ERR( L"DuplicateSoundBuffer", hr );
  340.                 goto LFail;
  341.             }
  342.         }
  343.     }
  344.     else
  345.     {
  346.         for( i=1; i<dwNumBuffers; i++ )
  347.         {
  348.             hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[i], NULL );
  349.             if( FAILED(hr) )
  350.             {
  351.                 DXUT_ERR( L"CreateSoundBuffer", hr );
  352.                 goto LFail;
  353.             }
  354.         }
  355.    }
  356.  
  357.     // Create the sound
  358.     *ppSound = new CSound( apDSBuffer, dwDSBufferSize, dwNumBuffers, pWaveFile, dwCreationFlags );
  359.  
  360.     SAFE_DELETE_ARRAY( apDSBuffer );
  361.     return S_OK;
  362.  
  363. LFail:
  364.     // Cleanup
  365.    
  366.     SAFE_DELETE_ARRAY( apDSBuffer );
  367.     return hr;
  368. }
  369.  
  370.  
  371. //-----------------------------------------------------------------------------
  372. // Name: CSoundManager::CreateStreaming()
  373. // Desc: 
  374. //-----------------------------------------------------------------------------
  375. HRESULT CSoundManager::CreateStreaming( CStreamingSound** ppStreamingSound, 
  376.                                         LPWSTR strWaveFileName, 
  377.                                         DWORD dwCreationFlags, 
  378.                                         GUID guid3DAlgorithm,
  379.                                         DWORD dwNotifyCount, 
  380.                                         DWORD dwNotifySize, 
  381.                                         HANDLE hNotifyEvent )
  382. {
  383.     HRESULT hr;
  384.  
  385.     if( m_pDS == NULL )
  386.         return CO_E_NOTINITIALIZED;
  387.     if( strWaveFileName == NULL || ppStreamingSound == NULL || hNotifyEvent == NULL )
  388.         return E_INVALIDARG;
  389.  
  390.     LPDIRECTSOUNDBUFFER pDSBuffer      = NULL;
  391.     DWORD               dwDSBufferSize = NULL;
  392.     CWaveFile*          pWaveFile      = NULL;
  393.     DSBPOSITIONNOTIFY*  aPosNotify     = NULL; 
  394.     LPDIRECTSOUNDNOTIFY pDSNotify      = NULL;
  395.  
  396.     pWaveFile = new CWaveFile();
  397.     if( pWaveFile == NULL )
  398.         return E_OUTOFMEMORY;
  399.     pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ );
  400.  
  401.     // Figure out how big the DSound buffer should be 
  402.     dwDSBufferSize = dwNotifySize * dwNotifyCount;
  403.  
  404.     // Set up the direct sound buffer.  Request the NOTIFY flag, so
  405.     // that we are notified as the sound buffer plays.  Note, that using this flag
  406.     // may limit the amount of hardware acceleration that can occur. 
  407.     DSBUFFERDESC dsbd;
  408.     ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
  409.     dsbd.dwSize          = sizeof(DSBUFFERDESC);
  410.     dsbd.dwFlags         = dwCreationFlags | 
  411.                            DSBCAPS_CTRLPOSITIONNOTIFY | 
  412.                            DSBCAPS_GETCURRENTPOSITION2;
  413.     dsbd.dwBufferBytes   = dwDSBufferSize;
  414.     dsbd.guid3DAlgorithm = guid3DAlgorithm;
  415.     dsbd.lpwfxFormat     = pWaveFile->m_pwfx;
  416.  
  417.     if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBuffer, NULL ) ) )
  418.     {
  419.         // If wave format isn't then it will return 
  420.         // either DSERR_BADFORMAT or E_INVALIDARG
  421.         if( hr == DSERR_BADFORMAT || hr == E_INVALIDARG )
  422.             return DXUT_ERR( L"CreateSoundBuffer", hr );
  423.  
  424.         return DXUT_ERR( L"CreateSoundBuffer", hr );
  425.     }
  426.  
  427.     // Create the notification events, so that we know when to fill
  428.     // the buffer as the sound plays. 
  429.     if( FAILED( hr = pDSBuffer->QueryInterface( IID_IDirectSoundNotify, 
  430.                                                 (VOID**)&pDSNotify ) ) )
  431.     {
  432.         SAFE_DELETE_ARRAY( aPosNotify );
  433.         return DXUT_ERR( L"QueryInterface", hr );
  434.     }
  435.  
  436.     aPosNotify = new DSBPOSITIONNOTIFY[ dwNotifyCount ];
  437.     if( aPosNotify == NULL )
  438.         return E_OUTOFMEMORY;
  439.  
  440.     for( DWORD i = 0; i < dwNotifyCount; i++ )
  441.     {
  442.         aPosNotify[i].dwOffset     = (dwNotifySize * i) + dwNotifySize - 1;
  443.         aPosNotify[i].hEventNotify = hNotifyEvent;             
  444.     }
  445.     
  446.     // Tell DirectSound when to notify us. The notification will come in the from 
  447.     // of signaled events that are handled in WinMain()
  448.     if( FAILED( hr = pDSNotify->SetNotificationPositions( dwNotifyCount, 
  449.                                                           aPosNotify ) ) )
  450.     {
  451.         SAFE_RELEASE( pDSNotify );
  452.         SAFE_DELETE_ARRAY( aPosNotify );
  453.         return DXUT_ERR( L"SetNotificationPositions", hr );
  454.     }
  455.  
  456.     SAFE_RELEASE( pDSNotify );
  457.     SAFE_DELETE_ARRAY( aPosNotify );
  458.  
  459.     // Create the sound
  460.     *ppStreamingSound = new CStreamingSound( pDSBuffer, dwDSBufferSize, pWaveFile, dwNotifySize );
  461.  
  462.     return S_OK;
  463. }
  464.  
  465.  
  466. //-----------------------------------------------------------------------------
  467. // Name: CSound::CSound()
  468. // Desc: Constructs the class
  469. //-----------------------------------------------------------------------------
  470. CSound::CSound( LPDIRECTSOUNDBUFFER* apDSBuffer, DWORD dwDSBufferSize, 
  471.                 DWORD dwNumBuffers, CWaveFile* pWaveFile, DWORD dwCreationFlags )
  472. {
  473.     DWORD i;
  474.  
  475.     m_apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers];
  476.     if( NULL != m_apDSBuffer )
  477.     {
  478.         for( i=0; i<dwNumBuffers; i++ )
  479.             m_apDSBuffer[i] = apDSBuffer[i];
  480.     
  481.         m_dwDSBufferSize = dwDSBufferSize;
  482.         m_dwNumBuffers   = dwNumBuffers;
  483.         m_pWaveFile      = pWaveFile;
  484.         m_dwCreationFlags = dwCreationFlags;
  485.         
  486.         FillBufferWithSound( m_apDSBuffer[0], FALSE );
  487.     }
  488. }
  489.  
  490.  
  491. //-----------------------------------------------------------------------------
  492. // Name: CSound::~CSound()
  493. // Desc: Destroys the class
  494. //-----------------------------------------------------------------------------
  495. CSound::~CSound()
  496. {
  497.     for( DWORD i=0; i<m_dwNumBuffers; i++ )
  498.     {
  499.         SAFE_RELEASE( m_apDSBuffer[i] ); 
  500.     }
  501.  
  502.     SAFE_DELETE_ARRAY( m_apDSBuffer ); 
  503.     SAFE_DELETE( m_pWaveFile );
  504. }
  505.  
  506.  
  507. //-----------------------------------------------------------------------------
  508. // Name: CSound::FillBufferWithSound()
  509. // Desc: Fills a DirectSound buffer with a sound file 
  510. //-----------------------------------------------------------------------------
  511. HRESULT CSound::FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB, BOOL bRepeatWavIfBufferLarger )
  512. {
  513.     HRESULT hr; 
  514.     VOID*   pDSLockedBuffer      = NULL; // Pointer to locked buffer memory
  515.     DWORD   dwDSLockedBufferSize = 0;    // Size of the locked DirectSound buffer
  516.     DWORD   dwWavDataRead        = 0;    // Amount of data read from the wav file 
  517.  
  518.     if( pDSB == NULL )
  519.         return CO_E_NOTINITIALIZED;
  520.  
  521.     // Make sure we have focus, and we didn't just switch in from
  522.     // an app which had a DirectSound device
  523.     if( FAILED( hr = RestoreBuffer( pDSB, NULL ) ) ) 
  524.         return DXUT_ERR( L"RestoreBuffer", hr );
  525.  
  526.     // Lock the buffer down
  527.     if( FAILED( hr = pDSB->Lock( 0, m_dwDSBufferSize, 
  528.                                  &pDSLockedBuffer, &dwDSLockedBufferSize, 
  529.                                  NULL, NULL, 0L ) ) )
  530.         return DXUT_ERR( L"Lock", hr );
  531.  
  532.     // Reset the wave file to the beginning 
  533.     m_pWaveFile->ResetFile();
  534.  
  535.     if( FAILED( hr = m_pWaveFile->Read( (BYTE*) pDSLockedBuffer,
  536.                                         dwDSLockedBufferSize, 
  537.                                         &dwWavDataRead ) ) )           
  538.         return DXUT_ERR( L"Read", hr );
  539.  
  540.     if( dwWavDataRead == 0 )
  541.     {
  542.         // Wav is blank, so just fill with silence
  543.         FillMemory( (BYTE*) pDSLockedBuffer, 
  544.                     dwDSLockedBufferSize, 
  545.                     (BYTE)(m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
  546.     }
  547.     else if( dwWavDataRead < dwDSLockedBufferSize )
  548.     {
  549.         // If the wav file was smaller than the DirectSound buffer, 
  550.         // we need to fill the remainder of the buffer with data 
  551.         if( bRepeatWavIfBufferLarger )
  552.         {       
  553.             // Reset the file and fill the buffer with wav data
  554.             DWORD dwReadSoFar = dwWavDataRead;    // From previous call above.
  555.             while( dwReadSoFar < dwDSLockedBufferSize )
  556.             {  
  557.                 // This will keep reading in until the buffer is full 
  558.                 // for very short files
  559.                 if( FAILED( hr = m_pWaveFile->ResetFile() ) )
  560.                     return DXUT_ERR( L"ResetFile", hr );
  561.  
  562.                 hr = m_pWaveFile->Read( (BYTE*)pDSLockedBuffer + dwReadSoFar,
  563.                                         dwDSLockedBufferSize - dwReadSoFar,
  564.                                         &dwWavDataRead );
  565.                 if( FAILED(hr) )
  566.                     return DXUT_ERR( L"Read", hr );
  567.  
  568.                 dwReadSoFar += dwWavDataRead;
  569.             } 
  570.         }
  571.         else
  572.         {
  573.             // Don't repeat the wav file, just fill in silence 
  574.             FillMemory( (BYTE*) pDSLockedBuffer + dwWavDataRead, 
  575.                         dwDSLockedBufferSize - dwWavDataRead, 
  576.                         (BYTE)(m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
  577.         }
  578.     }
  579.  
  580.     // Unlock the buffer, we don't need it anymore.
  581.     pDSB->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
  582.  
  583.     return S_OK;
  584. }
  585.  
  586.  
  587. //-----------------------------------------------------------------------------
  588. // Name: CSound::RestoreBuffer()
  589. // Desc: Restores the lost buffer. *pbWasRestored returns TRUE if the buffer was 
  590. //       restored.  It can also NULL if the information is not needed.
  591. //-----------------------------------------------------------------------------
  592. HRESULT CSound::RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, BOOL* pbWasRestored )
  593. {
  594.     HRESULT hr;
  595.  
  596.     if( pDSB == NULL )
  597.         return CO_E_NOTINITIALIZED;
  598.     if( pbWasRestored )
  599.         *pbWasRestored = FALSE;
  600.  
  601.     DWORD dwStatus;
  602.     if( FAILED( hr = pDSB->GetStatus( &dwStatus ) ) )
  603.         return DXUT_ERR( L"GetStatus", hr );
  604.  
  605.     if( dwStatus & DSBSTATUS_BUFFERLOST )
  606.     {
  607.         // Since the app could have just been activated, then
  608.         // DirectSound may not be giving us control yet, so 
  609.         // the restoring the buffer may fail.  
  610.         // If it does, sleep until DirectSound gives us control.
  611.         do 
  612.         {
  613.             hr = pDSB->Restore();
  614.             if( hr == DSERR_BUFFERLOST )
  615.                 Sleep( 10 );
  616.         }
  617.         while( ( hr = pDSB->Restore() ) == DSERR_BUFFERLOST );
  618.  
  619.         if( pbWasRestored != NULL )
  620.             *pbWasRestored = TRUE;
  621.  
  622.         return S_OK;
  623.     }
  624.     else
  625.     {
  626.         return S_FALSE;
  627.     }
  628. }
  629.  
  630.  
  631. //-----------------------------------------------------------------------------
  632. // Name: CSound::GetFreeBuffer()
  633. // Desc: Finding the first buffer that is not playing and return a pointer to 
  634. //       it, or if all are playing return a pointer to a randomly selected buffer.
  635. //-----------------------------------------------------------------------------
  636. LPDIRECTSOUNDBUFFER CSound::GetFreeBuffer()
  637. {
  638.     if( m_apDSBuffer == NULL )
  639.         return FALSE; 
  640.  
  641.     for( DWORD i=0; i<m_dwNumBuffers; i++ )
  642.     {
  643.         if( m_apDSBuffer[i] )
  644.         {  
  645.             DWORD dwStatus = 0;
  646.             m_apDSBuffer[i]->GetStatus( &dwStatus );
  647.             if ( ( dwStatus & DSBSTATUS_PLAYING ) == 0 )
  648.                 break;
  649.         }
  650.     }
  651.  
  652.     if( i != m_dwNumBuffers )
  653.         return m_apDSBuffer[ i ];
  654.     else
  655.         return m_apDSBuffer[ rand() % m_dwNumBuffers ];
  656. }
  657.  
  658.  
  659. //-----------------------------------------------------------------------------
  660. // Name: CSound::GetBuffer()
  661. // Desc: 
  662. //-----------------------------------------------------------------------------
  663. LPDIRECTSOUNDBUFFER CSound::GetBuffer( DWORD dwIndex )
  664. {
  665.     if( m_apDSBuffer == NULL )
  666.         return NULL;
  667.     if( dwIndex >= m_dwNumBuffers )
  668.         return NULL;
  669.  
  670.     return m_apDSBuffer[dwIndex];
  671. }
  672.  
  673.  
  674. //-----------------------------------------------------------------------------
  675. // Name: CSound::Get3DBufferInterface()
  676. // Desc: 
  677. //-----------------------------------------------------------------------------
  678. HRESULT CSound::Get3DBufferInterface( DWORD dwIndex, LPDIRECTSOUND3DBUFFER* ppDS3DBuffer )
  679. {
  680.     if( m_apDSBuffer == NULL )
  681.         return CO_E_NOTINITIALIZED;
  682.     if( dwIndex >= m_dwNumBuffers )
  683.         return E_INVALIDARG;
  684.  
  685.     *ppDS3DBuffer = NULL;
  686.  
  687.     return m_apDSBuffer[dwIndex]->QueryInterface( IID_IDirectSound3DBuffer, 
  688.                                                   (VOID**)ppDS3DBuffer );
  689. }
  690.  
  691.  
  692. //-----------------------------------------------------------------------------
  693. // Name: CSound::Play()
  694. // Desc: Plays the sound using voice management flags.  Pass in DSBPLAY_LOOPING
  695. //       in the dwFlags to loop the sound
  696. //-----------------------------------------------------------------------------
  697. HRESULT CSound::Play( DWORD dwPriority, DWORD dwFlags, LONG lVolume, LONG lFrequency, LONG lPan )
  698. {
  699.     HRESULT hr;
  700.     BOOL    bRestored;
  701.  
  702.     if( m_apDSBuffer == NULL )
  703.         return CO_E_NOTINITIALIZED;
  704.  
  705.     LPDIRECTSOUNDBUFFER pDSB = GetFreeBuffer();
  706.  
  707.     if( pDSB == NULL )
  708.         return DXUT_ERR( L"GetFreeBuffer", E_FAIL );
  709.  
  710.     // Restore the buffer if it was lost
  711.     if( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) )
  712.         return DXUT_ERR( L"RestoreBuffer", hr );
  713.  
  714.     if( bRestored )
  715.     {
  716.         // The buffer was restored, so we need to fill it with new data
  717.         if( FAILED( hr = FillBufferWithSound( pDSB, FALSE ) ) )
  718.             return DXUT_ERR( L"FillBufferWithSound", hr );
  719.     }
  720.  
  721.     if( m_dwCreationFlags & DSBCAPS_CTRLVOLUME )
  722.     {
  723.         pDSB->SetVolume( lVolume );
  724.     }
  725.  
  726.     if( lFrequency != -1 && 
  727.         (m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY) )
  728.     {
  729.         pDSB->SetFrequency( lFrequency );
  730.     }
  731.     
  732.     if( m_dwCreationFlags & DSBCAPS_CTRLPAN )
  733.     {
  734.         pDSB->SetPan( lPan );
  735.     }
  736.     
  737.     return pDSB->Play( 0, dwPriority, dwFlags );
  738. }
  739.  
  740.  
  741. //-----------------------------------------------------------------------------
  742. // Name: CSound::Play3D()
  743. // Desc: Plays the sound using voice management flags.  Pass in DSBPLAY_LOOPING
  744. //       in the dwFlags to loop the sound
  745. //-----------------------------------------------------------------------------
  746. HRESULT CSound::Play3D( LPDS3DBUFFER p3DBuffer, DWORD dwPriority, DWORD dwFlags, LONG lFrequency )
  747. {
  748.     HRESULT hr;
  749.     BOOL    bRestored;
  750.     DWORD   dwBaseFrequency;
  751.  
  752.     if( m_apDSBuffer == NULL )
  753.         return CO_E_NOTINITIALIZED;
  754.  
  755.     LPDIRECTSOUNDBUFFER pDSB = GetFreeBuffer();
  756.     if( pDSB == NULL )
  757.         return DXUT_ERR( L"GetFreeBuffer", E_FAIL );
  758.  
  759.     // Restore the buffer if it was lost
  760.     if( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) )
  761.         return DXUT_ERR( L"RestoreBuffer", hr );
  762.  
  763.     if( bRestored )
  764.     {
  765.         // The buffer was restored, so we need to fill it with new data
  766.         if( FAILED( hr = FillBufferWithSound( pDSB, FALSE ) ) )
  767.             return DXUT_ERR( L"FillBufferWithSound", hr );
  768.     }
  769.  
  770.     if( m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY )
  771.     {
  772.         pDSB->GetFrequency( &dwBaseFrequency );
  773.         pDSB->SetFrequency( dwBaseFrequency + lFrequency );
  774.     }
  775.  
  776.     // QI for the 3D buffer 
  777.     LPDIRECTSOUND3DBUFFER pDS3DBuffer;
  778.     hr = pDSB->QueryInterface( IID_IDirectSound3DBuffer, (VOID**) &pDS3DBuffer );
  779.     if( SUCCEEDED( hr ) )
  780.     {
  781.         hr = pDS3DBuffer->SetAllParameters( p3DBuffer, DS3D_IMMEDIATE );
  782.         if( SUCCEEDED( hr ) )
  783.         {
  784.             hr = pDSB->Play( 0, dwPriority, dwFlags );
  785.         }
  786.  
  787.         pDS3DBuffer->Release();
  788.     }
  789.  
  790.     return hr;
  791. }
  792.  
  793.  
  794. //-----------------------------------------------------------------------------
  795. // Name: CSound::Stop()
  796. // Desc: Stops the sound from playing
  797. //-----------------------------------------------------------------------------
  798. HRESULT CSound::Stop()
  799. {
  800.     if( m_apDSBuffer == NULL )
  801.         return CO_E_NOTINITIALIZED;
  802.  
  803.     HRESULT hr = 0;
  804.  
  805.     for( DWORD i=0; i<m_dwNumBuffers; i++ )
  806.         hr |= m_apDSBuffer[i]->Stop();
  807.  
  808.     return hr;
  809. }
  810.  
  811.  
  812. //-----------------------------------------------------------------------------
  813. // Name: CSound::Reset()
  814. // Desc: Reset all of the sound buffers
  815. //-----------------------------------------------------------------------------
  816. HRESULT CSound::Reset()
  817. {
  818.     if( m_apDSBuffer == NULL )
  819.         return CO_E_NOTINITIALIZED;
  820.  
  821.     HRESULT hr = 0;
  822.  
  823.     for( DWORD i=0; i<m_dwNumBuffers; i++ )
  824.         hr |= m_apDSBuffer[i]->SetCurrentPosition( 0 );
  825.  
  826.     return hr;
  827. }
  828.  
  829.  
  830. //-----------------------------------------------------------------------------
  831. // Name: CSound::IsSoundPlaying()
  832. // Desc: Checks to see if a buffer is playing and returns TRUE if it is.
  833. //-----------------------------------------------------------------------------
  834. BOOL CSound::IsSoundPlaying()
  835. {
  836.     BOOL bIsPlaying = FALSE;
  837.  
  838.     if( m_apDSBuffer == NULL )
  839.         return FALSE; 
  840.  
  841.     for( DWORD i=0; i<m_dwNumBuffers; i++ )
  842.     {
  843.         if( m_apDSBuffer[i] )
  844.         {  
  845.             DWORD dwStatus = 0;
  846.             m_apDSBuffer[i]->GetStatus( &dwStatus );
  847.             bIsPlaying |= ( ( dwStatus & DSBSTATUS_PLAYING ) != 0 );
  848.         }
  849.     }
  850.  
  851.     return bIsPlaying;
  852. }
  853.  
  854.  
  855. //-----------------------------------------------------------------------------
  856. // Name: CStreamingSound::CStreamingSound()
  857. // Desc: Setups up a buffer so data can be streamed from the wave file into 
  858. //       a buffer.  This is very useful for large wav files that would take a 
  859. //       while to load.  The buffer is initially filled with data, then 
  860. //       as sound is played the notification events are signaled and more data
  861. //       is written into the buffer by calling HandleWaveStreamNotification()
  862. //-----------------------------------------------------------------------------
  863. CStreamingSound::CStreamingSound( LPDIRECTSOUNDBUFFER pDSBuffer, DWORD dwDSBufferSize, 
  864.                                   CWaveFile* pWaveFile, DWORD dwNotifySize ) 
  865.                 : CSound( &pDSBuffer, dwDSBufferSize, 1, pWaveFile, 0 )           
  866. {
  867.     m_dwLastPlayPos     = 0;
  868.     m_dwPlayProgress    = 0;
  869.     m_dwNotifySize      = dwNotifySize;
  870.     m_dwNextWriteOffset = 0;
  871.     m_bFillNextNotificationWithSilence = FALSE;
  872. }
  873.  
  874.  
  875. //-----------------------------------------------------------------------------
  876. // Name: CStreamingSound::~CStreamingSound()
  877. // Desc: Destroys the class
  878. //-----------------------------------------------------------------------------
  879. CStreamingSound::~CStreamingSound()
  880. {
  881. }
  882.  
  883.  
  884. //-----------------------------------------------------------------------------
  885. // Name: CStreamingSound::HandleWaveStreamNotification()
  886. // Desc: Handle the notification that tells us to put more wav data in the 
  887. //       circular buffer
  888. //-----------------------------------------------------------------------------
  889. HRESULT CStreamingSound::HandleWaveStreamNotification( BOOL bLoopedPlay )
  890. {
  891.     HRESULT hr;
  892.     DWORD   dwCurrentPlayPos;
  893.     DWORD   dwPlayDelta;
  894.     DWORD   dwBytesWrittenToBuffer;
  895.     VOID*   pDSLockedBuffer = NULL;
  896.     VOID*   pDSLockedBuffer2 = NULL;
  897.     DWORD   dwDSLockedBufferSize;
  898.     DWORD   dwDSLockedBufferSize2;
  899.  
  900.     if( m_apDSBuffer == NULL || m_pWaveFile == NULL )
  901.         return CO_E_NOTINITIALIZED;
  902.  
  903.     // Restore the buffer if it was lost
  904.     BOOL bRestored;
  905.     if( FAILED( hr = RestoreBuffer( m_apDSBuffer[0], &bRestored ) ) )
  906.         return DXUT_ERR( L"RestoreBuffer", hr );
  907.  
  908.     if( bRestored )
  909.     {
  910.         // The buffer was restored, so we need to fill it with new data
  911.         if( FAILED( hr = FillBufferWithSound( m_apDSBuffer[0], FALSE ) ) )
  912.             return DXUT_ERR( L"FillBufferWithSound", hr );
  913.         return S_OK;
  914.     }
  915.  
  916.     // Lock the DirectSound buffer
  917.     if( FAILED( hr = m_apDSBuffer[0]->Lock( m_dwNextWriteOffset, m_dwNotifySize, 
  918.                                             &pDSLockedBuffer, &dwDSLockedBufferSize, 
  919.                                             &pDSLockedBuffer2, &dwDSLockedBufferSize2, 0L ) ) )
  920.         return DXUT_ERR( L"Lock", hr );
  921.  
  922.     // m_dwDSBufferSize and m_dwNextWriteOffset are both multiples of m_dwNotifySize, 
  923.     // it should the second buffer, so it should never be valid
  924.     if( pDSLockedBuffer2 != NULL )
  925.         return E_UNEXPECTED; 
  926.  
  927.     if( !m_bFillNextNotificationWithSilence )
  928.     {
  929.         // Fill the DirectSound buffer with wav data
  930.         if( FAILED( hr = m_pWaveFile->Read( (BYTE*) pDSLockedBuffer, 
  931.                                                   dwDSLockedBufferSize, 
  932.                                                   &dwBytesWrittenToBuffer ) ) )           
  933.             return DXUT_ERR( L"Read", hr );
  934.     }
  935.     else
  936.     {
  937.         // Fill the DirectSound buffer with silence
  938.         FillMemory( pDSLockedBuffer, dwDSLockedBufferSize, 
  939.                     (BYTE)( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
  940.         dwBytesWrittenToBuffer = dwDSLockedBufferSize;
  941.     }
  942.  
  943.     // If the number of bytes written is less than the 
  944.     // amount we requested, we have a short file.
  945.     if( dwBytesWrittenToBuffer < dwDSLockedBufferSize )
  946.     {
  947.         if( !bLoopedPlay ) 
  948.         {
  949.             // Fill in silence for the rest of the buffer.
  950.             FillMemory( (BYTE*) pDSLockedBuffer + dwBytesWrittenToBuffer, 
  951.                         dwDSLockedBufferSize - dwBytesWrittenToBuffer, 
  952.                         (BYTE)(m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
  953.  
  954.             // Any future notifications should just fill the buffer with silence
  955.             m_bFillNextNotificationWithSilence = TRUE;
  956.         }
  957.         else
  958.         {
  959.             // We are looping, so reset the file and fill the buffer with wav data
  960.             DWORD dwReadSoFar = dwBytesWrittenToBuffer;    // From previous call above.
  961.             while( dwReadSoFar < dwDSLockedBufferSize )
  962.             {  
  963.                 // This will keep reading in until the buffer is full (for very short files).
  964.                 if( FAILED( hr = m_pWaveFile->ResetFile() ) )
  965.                     return DXUT_ERR( L"ResetFile", hr );
  966.  
  967.                 if( FAILED( hr = m_pWaveFile->Read( (BYTE*)pDSLockedBuffer + dwReadSoFar,
  968.                                                           dwDSLockedBufferSize - dwReadSoFar,
  969.                                                           &dwBytesWrittenToBuffer ) ) )
  970.                     return DXUT_ERR( L"Read", hr );
  971.  
  972.                 dwReadSoFar += dwBytesWrittenToBuffer;
  973.             } 
  974.         } 
  975.     }
  976.  
  977.     // Unlock the DirectSound buffer
  978.     m_apDSBuffer[0]->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
  979.  
  980.     // Figure out how much data has been played so far.  When we have played
  981.     // past the end of the file, we will either need to start filling the
  982.     // buffer with silence or starting reading from the beginning of the file, 
  983.     // depending if the user wants to loop the sound
  984.     if( FAILED( hr = m_apDSBuffer[0]->GetCurrentPosition( &dwCurrentPlayPos, NULL ) ) )
  985.         return DXUT_ERR( L"GetCurrentPosition", hr );
  986.  
  987.     // Check to see if the position counter looped
  988.     if( dwCurrentPlayPos < m_dwLastPlayPos )
  989.         dwPlayDelta = ( m_dwDSBufferSize - m_dwLastPlayPos ) + dwCurrentPlayPos;
  990.     else
  991.         dwPlayDelta = dwCurrentPlayPos - m_dwLastPlayPos;
  992.  
  993.     m_dwPlayProgress += dwPlayDelta;
  994.     m_dwLastPlayPos = dwCurrentPlayPos;
  995.  
  996.     // If we are now filling the buffer with silence, then we have found the end so 
  997.     // check to see if the entire sound has played, if it has then stop the buffer.
  998.     if( m_bFillNextNotificationWithSilence )
  999.     {
  1000.         // We don't want to cut off the sound before it's done playing.
  1001.         if( m_dwPlayProgress >= m_pWaveFile->GetSize() )
  1002.         {
  1003.             m_apDSBuffer[0]->Stop();
  1004.         }
  1005.     }
  1006.  
  1007.     // Update where the buffer will lock (for next time)
  1008.     m_dwNextWriteOffset += dwDSLockedBufferSize; 
  1009.     m_dwNextWriteOffset %= m_dwDSBufferSize; // Circular buffer
  1010.  
  1011.     return S_OK;
  1012. }
  1013.  
  1014.  
  1015. //-----------------------------------------------------------------------------
  1016. // Name: CStreamingSound::Reset()
  1017. // Desc: Resets the sound so it will begin playing at the beginning
  1018. //-----------------------------------------------------------------------------
  1019. HRESULT CStreamingSound::Reset()
  1020. {
  1021.     HRESULT hr;
  1022.  
  1023.     if( m_apDSBuffer[0] == NULL || m_pWaveFile == NULL )
  1024.         return CO_E_NOTINITIALIZED;
  1025.  
  1026.     m_dwLastPlayPos     = 0;
  1027.     m_dwPlayProgress    = 0;
  1028.     m_dwNextWriteOffset = 0;
  1029.     m_bFillNextNotificationWithSilence = FALSE;
  1030.  
  1031.     // Restore the buffer if it was lost
  1032.     BOOL bRestored;
  1033.     if( FAILED( hr = RestoreBuffer( m_apDSBuffer[0], &bRestored ) ) )
  1034.         return DXUT_ERR( L"RestoreBuffer", hr );
  1035.  
  1036.     if( bRestored )
  1037.     {
  1038.         // The buffer was restored, so we need to fill it with new data
  1039.         if( FAILED( hr = FillBufferWithSound( m_apDSBuffer[0], FALSE ) ) )
  1040.             return DXUT_ERR( L"FillBufferWithSound", hr );
  1041.     }
  1042.  
  1043.     m_pWaveFile->ResetFile();
  1044.  
  1045.     return m_apDSBuffer[0]->SetCurrentPosition( 0L );  
  1046. }
  1047.  
  1048.  
  1049. //-----------------------------------------------------------------------------
  1050. // Name: CWaveFile::CWaveFile()
  1051. // Desc: Constructs the class.  Call Open() to open a wave file for reading.  
  1052. //       Then call Read() as needed.  Calling the destructor or Close() 
  1053. //       will close the file.  
  1054. //-----------------------------------------------------------------------------
  1055. CWaveFile::CWaveFile()
  1056. {
  1057.     m_pwfx    = NULL;
  1058.     m_hmmio   = NULL;
  1059.     m_pResourceBuffer = NULL;
  1060.     m_dwSize  = 0;
  1061.     m_bIsReadingFromMemory = FALSE;
  1062. }
  1063.  
  1064.  
  1065. //-----------------------------------------------------------------------------
  1066. // Name: CWaveFile::~CWaveFile()
  1067. // Desc: Destructs the class
  1068. //-----------------------------------------------------------------------------
  1069. CWaveFile::~CWaveFile()
  1070. {
  1071.     Close();
  1072.  
  1073.     if( !m_bIsReadingFromMemory )
  1074.         SAFE_DELETE_ARRAY( m_pwfx );
  1075. }
  1076.  
  1077.  
  1078. //-----------------------------------------------------------------------------
  1079. // Name: CWaveFile::Open()
  1080. // Desc: Opens a wave file for reading
  1081. //-----------------------------------------------------------------------------
  1082. HRESULT CWaveFile::Open( LPWSTR strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags )
  1083. {
  1084.     HRESULT hr;
  1085.  
  1086.     m_dwFlags = dwFlags;
  1087.     m_bIsReadingFromMemory = FALSE;
  1088.  
  1089.     if( m_dwFlags == WAVEFILE_READ )
  1090.     {
  1091.         if( strFileName == NULL )
  1092.             return E_INVALIDARG;
  1093.         SAFE_DELETE_ARRAY( m_pwfx );
  1094.  
  1095.         m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF | MMIO_READ );
  1096.  
  1097.         if( NULL == m_hmmio )
  1098.         {
  1099.             HRSRC   hResInfo;
  1100.             HGLOBAL hResData;
  1101.             DWORD   dwSize;
  1102.             VOID*   pvRes;
  1103.  
  1104.             // Loading it as a file failed, so try it as a resource
  1105.             if( NULL == ( hResInfo = FindResource( NULL, strFileName, L"WAVE" ) ) )
  1106.             {
  1107.                 if( NULL == ( hResInfo = FindResource( NULL, strFileName, L"WAV" ) ) )
  1108.                     return DXUT_ERR( L"FindResource", E_FAIL );
  1109.             }
  1110.  
  1111.             if( NULL == ( hResData = LoadResource( NULL, hResInfo ) ) )
  1112.                 return DXUT_ERR( L"LoadResource", E_FAIL );
  1113.  
  1114.             if( 0 == ( dwSize = SizeofResource( NULL, hResInfo ) ) ) 
  1115.                 return DXUT_ERR( L"SizeofResource", E_FAIL );
  1116.  
  1117.             if( NULL == ( pvRes = LockResource( hResData ) ) )
  1118.                 return DXUT_ERR( L"LockResource", E_FAIL );
  1119.  
  1120.             m_pResourceBuffer = new CHAR[ dwSize ];
  1121.             memcpy( m_pResourceBuffer, pvRes, dwSize );
  1122.  
  1123.             MMIOINFO mmioInfo;
  1124.             ZeroMemory( &mmioInfo, sizeof(mmioInfo) );
  1125.             mmioInfo.fccIOProc = FOURCC_MEM;
  1126.             mmioInfo.cchBuffer = dwSize;
  1127.             mmioInfo.pchBuffer = (CHAR*) m_pResourceBuffer;
  1128.  
  1129.             m_hmmio = mmioOpen( NULL, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ );
  1130.         }
  1131.  
  1132.         if( FAILED( hr = ReadMMIO() ) )
  1133.         {
  1134.             // ReadMMIO will fail if its an not a wave file
  1135.             mmioClose( m_hmmio, 0 );
  1136.             return DXUT_ERR( L"ReadMMIO", hr );
  1137.         }
  1138.  
  1139.         if( FAILED( hr = ResetFile() ) )
  1140.             return DXUT_ERR( L"ResetFile", hr );
  1141.  
  1142.         // After the reset, the size of the wav file is m_ck.cksize so store it now
  1143.         m_dwSize = m_ck.cksize;
  1144.     }
  1145.     else
  1146.     {
  1147.         m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF  | 
  1148.                                                   MMIO_READWRITE | 
  1149.                                                   MMIO_CREATE );
  1150.         if( NULL == m_hmmio )
  1151.             return DXUT_ERR( L"mmioOpen", E_FAIL );
  1152.  
  1153.         if( FAILED( hr = WriteMMIO( pwfx ) ) )
  1154.         {
  1155.             mmioClose( m_hmmio, 0 );
  1156.             return DXUT_ERR( L"WriteMMIO", hr );
  1157.         }
  1158.                         
  1159.         if( FAILED( hr = ResetFile() ) )
  1160.             return DXUT_ERR( L"ResetFile", hr );
  1161.     }
  1162.  
  1163.     return hr;
  1164. }
  1165.  
  1166.  
  1167. //-----------------------------------------------------------------------------
  1168. // Name: CWaveFile::OpenFromMemory()
  1169. // Desc: copy data to CWaveFile member variable from memory
  1170. //-----------------------------------------------------------------------------
  1171. HRESULT CWaveFile::OpenFromMemory( BYTE* pbData, ULONG ulDataSize, 
  1172.                                    WAVEFORMATEX* pwfx, DWORD dwFlags )
  1173. {
  1174.     m_pwfx       = pwfx;
  1175.     m_ulDataSize = ulDataSize;
  1176.     m_pbData     = pbData;
  1177.     m_pbDataCur  = m_pbData;
  1178.     m_bIsReadingFromMemory = TRUE;
  1179.     
  1180.     if( dwFlags != WAVEFILE_READ )
  1181.         return E_NOTIMPL;       
  1182.     
  1183.     return S_OK;
  1184. }
  1185.  
  1186.  
  1187. //-----------------------------------------------------------------------------
  1188. // Name: CWaveFile::ReadMMIO()
  1189. // Desc: Support function for reading from a multimedia I/O stream.
  1190. //       m_hmmio must be valid before calling.  This function uses it to
  1191. //       update m_ckRiff, and m_pwfx. 
  1192. //-----------------------------------------------------------------------------
  1193. HRESULT CWaveFile::ReadMMIO()
  1194. {
  1195.     MMCKINFO        ckIn;           // chunk info. for general use.
  1196.     PCMWAVEFORMAT   pcmWaveFormat;  // Temp PCM structure to load in.       
  1197.  
  1198.     m_pwfx = NULL;
  1199.  
  1200.     if( ( 0 != mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) ) )
  1201.         return DXUT_ERR( L"mmioDescend", E_FAIL );
  1202.  
  1203.     // Check to make sure this is a valid wave file
  1204.     if( (m_ckRiff.ckid != FOURCC_RIFF) ||
  1205.         (m_ckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E') ) )
  1206.         return DXUT_ERR( L"mmioFOURCC", E_FAIL );
  1207.  
  1208.     // Search the input file for for the 'fmt ' chunk.
  1209.     ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
  1210.     if( 0 != mmioDescend( m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK ) )
  1211.         return DXUT_ERR( L"mmioDescend", E_FAIL );
  1212.  
  1213.     // Expect the 'fmt' chunk to be at least as large as <PCMWAVEFORMAT>;
  1214.     // if there are extra parameters at the end, we'll ignore them
  1215.        if( ckIn.cksize < (LONG) sizeof(PCMWAVEFORMAT) )
  1216.            return DXUT_ERR( L"sizeof(PCMWAVEFORMAT)", E_FAIL );
  1217.  
  1218.     // Read the 'fmt ' chunk into <pcmWaveFormat>.
  1219.     if( mmioRead( m_hmmio, (HPSTR) &pcmWaveFormat, 
  1220.                   sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat) )
  1221.         return DXUT_ERR( L"mmioRead", E_FAIL );
  1222.  
  1223.     // Allocate the waveformatex, but if its not pcm format, read the next
  1224.     // word, and thats how many extra bytes to allocate.
  1225.     if( pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM )
  1226.     {
  1227.         m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) ];
  1228.         if( NULL == m_pwfx )
  1229.             return DXUT_ERR( L"m_pwfx", E_FAIL );
  1230.  
  1231.         // Copy the bytes from the pcm structure to the waveformatex structure
  1232.         memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) );
  1233.         m_pwfx->cbSize = 0;
  1234.     }
  1235.     else
  1236.     {
  1237.         // Read in length of extra bytes.
  1238.         WORD cbExtraBytes = 0L;
  1239.         if( mmioRead( m_hmmio, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD) )
  1240.             return DXUT_ERR( L"mmioRead", E_FAIL );
  1241.  
  1242.         m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) + cbExtraBytes ];
  1243.         if( NULL == m_pwfx )
  1244.             return DXUT_ERR( L"new", E_FAIL );
  1245.  
  1246.         // Copy the bytes from the pcm structure to the waveformatex structure
  1247.         memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) );
  1248.         m_pwfx->cbSize = cbExtraBytes;
  1249.  
  1250.         // Now, read those extra bytes into the structure, if cbExtraAlloc != 0.
  1251.         if( mmioRead( m_hmmio, (CHAR*)(((BYTE*)&(m_pwfx->cbSize))+sizeof(WORD)),
  1252.                       cbExtraBytes ) != cbExtraBytes )
  1253.         {
  1254.             SAFE_DELETE( m_pwfx );
  1255.             return DXUT_ERR( L"mmioRead", E_FAIL );
  1256.         }
  1257.     }
  1258.  
  1259.     // Ascend the input file out of the 'fmt ' chunk.
  1260.     if( 0 != mmioAscend( m_hmmio, &ckIn, 0 ) )
  1261.     {
  1262.         SAFE_DELETE( m_pwfx );
  1263.         return DXUT_ERR( L"mmioAscend", E_FAIL );
  1264.     }
  1265.  
  1266.     return S_OK;
  1267. }
  1268.  
  1269.  
  1270. //-----------------------------------------------------------------------------
  1271. // Name: CWaveFile::GetSize()
  1272. // Desc: Retuns the size of the read access wave file 
  1273. //-----------------------------------------------------------------------------
  1274. DWORD CWaveFile::GetSize()
  1275. {
  1276.     return m_dwSize;
  1277. }
  1278.  
  1279.  
  1280. //-----------------------------------------------------------------------------
  1281. // Name: CWaveFile::ResetFile()
  1282. // Desc: Resets the internal m_ck pointer so reading starts from the 
  1283. //       beginning of the file again 
  1284. //-----------------------------------------------------------------------------
  1285. HRESULT CWaveFile::ResetFile()
  1286. {
  1287.     if( m_bIsReadingFromMemory )
  1288.     {
  1289.         m_pbDataCur = m_pbData;
  1290.     }
  1291.     else 
  1292.     {
  1293.         if( m_hmmio == NULL )
  1294.             return CO_E_NOTINITIALIZED;
  1295.  
  1296.         if( m_dwFlags == WAVEFILE_READ )
  1297.         {
  1298.             // Seek to the data
  1299.             if( -1 == mmioSeek( m_hmmio, m_ckRiff.dwDataOffset + sizeof(FOURCC),
  1300.                             SEEK_SET ) )
  1301.                 return DXUT_ERR( L"mmioSeek", E_FAIL );
  1302.  
  1303.             // Search the input file for the 'data' chunk.
  1304.             m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
  1305.             if( 0 != mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) )
  1306.               return DXUT_ERR( L"mmioDescend", E_FAIL );
  1307.         }
  1308.         else
  1309.         {
  1310.             // Create the 'data' chunk that holds the waveform samples.  
  1311.             m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
  1312.             m_ck.cksize = 0;
  1313.  
  1314.             if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) ) 
  1315.                 return DXUT_ERR( L"mmioCreateChunk", E_FAIL );
  1316.  
  1317.             if( 0 != mmioGetInfo( m_hmmio, &m_mmioinfoOut, 0 ) )
  1318.                 return DXUT_ERR( L"mmioGetInfo", E_FAIL );
  1319.         }
  1320.     }
  1321.     
  1322.     return S_OK;
  1323. }
  1324.  
  1325.  
  1326. //-----------------------------------------------------------------------------
  1327. // Name: CWaveFile::Read()
  1328. // Desc: Reads section of data from a wave file into pBuffer and returns 
  1329. //       how much read in pdwSizeRead, reading not more than dwSizeToRead.
  1330. //       This uses m_ck to determine where to start reading from.  So 
  1331. //       subsequent calls will be continue where the last left off unless 
  1332. //       Reset() is called.
  1333. //-----------------------------------------------------------------------------
  1334. HRESULT CWaveFile::Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead )
  1335. {
  1336.     if( m_bIsReadingFromMemory )
  1337.     {
  1338.         if( m_pbDataCur == NULL )
  1339.             return CO_E_NOTINITIALIZED;
  1340.         if( pdwSizeRead != NULL )
  1341.             *pdwSizeRead = 0;
  1342.  
  1343.         if( (BYTE*)(m_pbDataCur + dwSizeToRead) > 
  1344.             (BYTE*)(m_pbData + m_ulDataSize) )
  1345.         {
  1346.             dwSizeToRead = m_ulDataSize - (DWORD)(m_pbDataCur - m_pbData);
  1347.         }
  1348.         
  1349.         CopyMemory( pBuffer, m_pbDataCur, dwSizeToRead );
  1350.         
  1351.         if( pdwSizeRead != NULL )
  1352.             *pdwSizeRead = dwSizeToRead;
  1353.  
  1354.         return S_OK;
  1355.     }
  1356.     else 
  1357.     {
  1358.         MMIOINFO mmioinfoIn; // current status of m_hmmio
  1359.  
  1360.         if( m_hmmio == NULL )
  1361.             return CO_E_NOTINITIALIZED;
  1362.         if( pBuffer == NULL || pdwSizeRead == NULL )
  1363.             return E_INVALIDARG;
  1364.  
  1365.         if( pdwSizeRead != NULL )
  1366.             *pdwSizeRead = 0;
  1367.  
  1368.         if( 0 != mmioGetInfo( m_hmmio, &mmioinfoIn, 0 ) )
  1369.             return DXUT_ERR( L"mmioGetInfo", E_FAIL );
  1370.                 
  1371.         UINT cbDataIn = dwSizeToRead;
  1372.         if( cbDataIn > m_ck.cksize ) 
  1373.             cbDataIn = m_ck.cksize;       
  1374.  
  1375.         m_ck.cksize -= cbDataIn;
  1376.     
  1377.         for( DWORD cT = 0; cT < cbDataIn; cT++ )
  1378.         {
  1379.             // Copy the bytes from the io to the buffer.
  1380.             if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
  1381.             {
  1382.                 if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) )
  1383.                     return DXUT_ERR( L"mmioAdvance", E_FAIL );
  1384.  
  1385.                 if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
  1386.                     return DXUT_ERR( L"mmioinfoIn.pchNext", E_FAIL );
  1387.             }
  1388.  
  1389.             // Actual copy.
  1390.             *((BYTE*)pBuffer+cT) = *((BYTE*)mmioinfoIn.pchNext);
  1391.             mmioinfoIn.pchNext++;
  1392.         }
  1393.  
  1394.         if( 0 != mmioSetInfo( m_hmmio, &mmioinfoIn, 0 ) )
  1395.             return DXUT_ERR( L"mmioSetInfo", E_FAIL );
  1396.  
  1397.         if( pdwSizeRead != NULL )
  1398.             *pdwSizeRead = cbDataIn;
  1399.  
  1400.         return S_OK;
  1401.     }
  1402. }
  1403.  
  1404.  
  1405. //-----------------------------------------------------------------------------
  1406. // Name: CWaveFile::Close()
  1407. // Desc: Closes the wave file 
  1408. //-----------------------------------------------------------------------------
  1409. HRESULT CWaveFile::Close()
  1410. {
  1411.     if( m_dwFlags == WAVEFILE_READ )
  1412.     {
  1413.         mmioClose( m_hmmio, 0 );
  1414.         m_hmmio = NULL;
  1415.         SAFE_DELETE_ARRAY( m_pResourceBuffer );
  1416.     }
  1417.     else
  1418.     {
  1419.         m_mmioinfoOut.dwFlags |= MMIO_DIRTY;
  1420.  
  1421.         if( m_hmmio == NULL )
  1422.             return CO_E_NOTINITIALIZED;
  1423.  
  1424.         if( 0 != mmioSetInfo( m_hmmio, &m_mmioinfoOut, 0 ) )
  1425.             return DXUT_ERR( L"mmioSetInfo", E_FAIL );
  1426.     
  1427.         // Ascend the output file out of the 'data' chunk -- this will cause
  1428.         // the chunk size of the 'data' chunk to be written.
  1429.         if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) )
  1430.             return DXUT_ERR( L"mmioAscend", E_FAIL );
  1431.     
  1432.         // Do this here instead...
  1433.         if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) )
  1434.             return DXUT_ERR( L"mmioAscend", E_FAIL );
  1435.         
  1436.         mmioSeek( m_hmmio, 0, SEEK_SET );
  1437.  
  1438.         if( 0 != (INT)mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) )
  1439.             return DXUT_ERR( L"mmioDescend", E_FAIL );
  1440.     
  1441.         m_ck.ckid = mmioFOURCC('f', 'a', 'c', 't');
  1442.  
  1443.         if( 0 == mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) ) 
  1444.         {
  1445.             DWORD dwSamples = 0;
  1446.             mmioWrite( m_hmmio, (HPSTR)&dwSamples, sizeof(DWORD) );
  1447.             mmioAscend( m_hmmio, &m_ck, 0 ); 
  1448.         }
  1449.     
  1450.         // Ascend the output file out of the 'RIFF' chunk -- this will cause
  1451.         // the chunk size of the 'RIFF' chunk to be written.
  1452.         if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) )
  1453.             return DXUT_ERR( L"mmioAscend", E_FAIL );
  1454.     
  1455.         mmioClose( m_hmmio, 0 );
  1456.         m_hmmio = NULL;
  1457.     }
  1458.  
  1459.     return S_OK;
  1460. }
  1461.  
  1462.  
  1463. //-----------------------------------------------------------------------------
  1464. // Name: CWaveFile::WriteMMIO()
  1465. // Desc: Support function for reading from a multimedia I/O stream
  1466. //       pwfxDest is the WAVEFORMATEX for this new wave file.  
  1467. //       m_hmmio must be valid before calling.  This function uses it to
  1468. //       update m_ckRiff, and m_ck.  
  1469. //-----------------------------------------------------------------------------
  1470. HRESULT CWaveFile::WriteMMIO( WAVEFORMATEX *pwfxDest )
  1471. {
  1472.     DWORD    dwFactChunk; // Contains the actual fact chunk. Garbage until WaveCloseWriteFile.
  1473.     MMCKINFO ckOut1;
  1474.     
  1475.     dwFactChunk = (DWORD)-1;
  1476.  
  1477.     // Create the output file RIFF chunk of form type 'WAVE'.
  1478.     m_ckRiff.fccType = mmioFOURCC('W', 'A', 'V', 'E');       
  1479.     m_ckRiff.cksize = 0;
  1480.  
  1481.     if( 0 != mmioCreateChunk( m_hmmio, &m_ckRiff, MMIO_CREATERIFF ) )
  1482.         return DXUT_ERR( L"mmioCreateChunk", E_FAIL );
  1483.     
  1484.     // We are now descended into the 'RIFF' chunk we just created.
  1485.     // Now create the 'fmt ' chunk. Since we know the size of this chunk,
  1486.     // specify it in the MMCKINFO structure so MMIO doesn't have to seek
  1487.     // back and set the chunk size after ascending from the chunk.
  1488.     m_ck.ckid = mmioFOURCC('f', 'm', 't', ' ');
  1489.     m_ck.cksize = sizeof(PCMWAVEFORMAT);   
  1490.  
  1491.     if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) )
  1492.         return DXUT_ERR( L"mmioCreateChunk", E_FAIL );
  1493.     
  1494.     // Write the PCMWAVEFORMAT structure to the 'fmt ' chunk if its that type. 
  1495.     if( pwfxDest->wFormatTag == WAVE_FORMAT_PCM )
  1496.     {
  1497.         if( mmioWrite( m_hmmio, (HPSTR) pwfxDest, 
  1498.                        sizeof(PCMWAVEFORMAT)) != sizeof(PCMWAVEFORMAT))
  1499.             return DXUT_ERR( L"mmioWrite", E_FAIL );
  1500.     }   
  1501.     else 
  1502.     {
  1503.         // Write the variable length size.
  1504.         if( (UINT)mmioWrite( m_hmmio, (HPSTR) pwfxDest, 
  1505.                              sizeof(*pwfxDest) + pwfxDest->cbSize ) != 
  1506.                              ( sizeof(*pwfxDest) + pwfxDest->cbSize ) )
  1507.             return DXUT_ERR( L"mmioWrite", E_FAIL );
  1508.     }  
  1509.     
  1510.     // Ascend out of the 'fmt ' chunk, back into the 'RIFF' chunk.
  1511.     if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) )
  1512.         return DXUT_ERR( L"mmioAscend", E_FAIL );
  1513.     
  1514.     // Now create the fact chunk, not required for PCM but nice to have.  This is filled
  1515.     // in when the close routine is called.
  1516.     ckOut1.ckid = mmioFOURCC('f', 'a', 'c', 't');
  1517.     ckOut1.cksize = 0;
  1518.  
  1519.     if( 0 != mmioCreateChunk( m_hmmio, &ckOut1, 0 ) )
  1520.         return DXUT_ERR( L"mmioCreateChunk", E_FAIL );
  1521.     
  1522.     if( mmioWrite( m_hmmio, (HPSTR)&dwFactChunk, sizeof(dwFactChunk)) != 
  1523.                     sizeof(dwFactChunk) )
  1524.          return DXUT_ERR( L"mmioWrite", E_FAIL );
  1525.     
  1526.     // Now ascend out of the fact chunk...
  1527.     if( 0 != mmioAscend( m_hmmio, &ckOut1, 0 ) )
  1528.         return DXUT_ERR( L"mmioAscend", E_FAIL );
  1529.        
  1530.     return S_OK;
  1531. }
  1532.  
  1533.  
  1534. //-----------------------------------------------------------------------------
  1535. // Name: CWaveFile::Write()
  1536. // Desc: Writes data to the open wave file
  1537. //-----------------------------------------------------------------------------
  1538. HRESULT CWaveFile::Write( UINT nSizeToWrite, BYTE* pbSrcData, UINT* pnSizeWrote )
  1539. {
  1540.     UINT cT;
  1541.  
  1542.     if( m_bIsReadingFromMemory )
  1543.         return E_NOTIMPL;
  1544.     if( m_hmmio == NULL )
  1545.         return CO_E_NOTINITIALIZED;
  1546.     if( pnSizeWrote == NULL || pbSrcData == NULL )
  1547.         return E_INVALIDARG;
  1548.  
  1549.     *pnSizeWrote = 0;
  1550.     
  1551.     for( cT = 0; cT < nSizeToWrite; cT++ )
  1552.     {       
  1553.         if( m_mmioinfoOut.pchNext == m_mmioinfoOut.pchEndWrite )
  1554.         {
  1555.             m_mmioinfoOut.dwFlags |= MMIO_DIRTY;
  1556.             if( 0 != mmioAdvance( m_hmmio, &m_mmioinfoOut, MMIO_WRITE ) )
  1557.                 return DXUT_ERR( L"mmioAdvance", E_FAIL );
  1558.         }
  1559.  
  1560.         *((BYTE*)m_mmioinfoOut.pchNext) = *((BYTE*)pbSrcData+cT);
  1561.         (BYTE*)m_mmioinfoOut.pchNext++;
  1562.  
  1563.         (*pnSizeWrote)++;
  1564.     }
  1565.  
  1566.     return S_OK;
  1567. }
  1568.